package log

import (
	"encoding/json"
	"fmt"
	"gitee.com/amwfhv/tge/errors"
	"strings"

	"github.com/spf13/pflag"
	"go.uber.org/zap/zapcore"
)

const (
	flagEnableConsole     = "log.enable-console"
	flagFileName          = "log.filename"
	flagMaxSize           = "log.max-size"
	flagMaxAge            = "log.max-age"
	flagMaxFile           = "log.max-file"
	flagCompress          = "log.compress"
	flagLevel             = "log.level"
	flagDisableCaller     = "log.disable-caller"
	flagDisableStacktrace = "log.disable-stacktrace"
	flagFormat            = "log.format"
	flagEnableColor       = "log.enable-color"
	flagDevelopment       = "log.development"
	flagName              = "log.name"

	consoleFormat = "console"
	jsonFormat    = "json"
)

// 与日志相关的配置选项
type Options struct {
	// 是否输出到控制台
	EnableConsole bool `json:"enable-console" mapstructure:"enable-console"`
	// 日志文件名
	FileName string `json:"filename" mapstructure:"filename"`
	// 日志文件大小
	MaxSize int `json:"max-size" mapstructure:"max-size"`
	//保留文件数
	MaxFile int `json:"max-file" mapstructure:"max-file"`
	// 保留时间
	MaxAge int `json:"max-age" mapstructure:"max-age"`
	// 是否压缩
	Compress bool `json:"compress" mapstructure:"compress"`
	// 日志级别，优先级从低到高依次为：Debug, Info, Warn, Error, Dpanic, Panic, Fatal。
	Level string `json:"level"              mapstructure:"level"`
	// 支持的日志输出格式，目前支持 Console 和 JSON 两种。Console 其实就是 Text 格式。
	Format string `json:"format"             mapstructure:"format"`
	// 是否开启 caller，如果开启会在日志中显示调用日志所在的文件、函数和行号。
	DisableCaller bool `json:"disable-caller"     mapstructure:"disable-caller"`
	// 是否在 Panic 及以上级别禁止打印堆栈信息。
	DisableStacktrace bool `json:"disable-stacktrace" mapstructure:"disable-stacktrace"`
	// 是否开启颜色输出，true，是；false，否。
	EnableColor bool `json:"enable-color"       mapstructure:"enable-color"`
	// 是否是开发模式。如果是开发模式，会对 DPanicLevel 进行堆栈跟踪。
	Development bool `json:"development"        mapstructure:"development"`
	// Logger名字
	Name string `json:"name"               mapstructure:"name"`
}

// NewOptions 创建默认的日志选项
func NewOptions() *Options {
	return &Options{
		EnableConsole:     true,
		FileName:          "./logs/tge.log",
		MaxSize:           100,
		MaxFile:           0,
		MaxAge:            0,
		Compress:          false,
		Level:             zapcore.InfoLevel.String(),
		DisableCaller:     false,
		DisableStacktrace: false,
		Format:            consoleFormat,
		EnableColor:       false,
		Development:       false,
	}
}

// Validate 验证选项字段
func (o *Options) Validate() []error {
	var errs []error

	if o.FileName == "" {
		errs = append(errs, errors.New("log filename is empty"))
	}

	var zapLevel zapcore.Level
	if err := zapLevel.UnmarshalText([]byte(o.Level)); err != nil {
		errs = append(errs, err)
	}

	format := strings.ToLower(o.Format)
	if format != consoleFormat && format != jsonFormat {
		errs = append(errs, fmt.Errorf("not a valid log format: %q", o.Format))
	}

	return errs
}

// AddFlags 将日志相关的选项，添加到启动参数中
func (o *Options) AddFlags(fs *pflag.FlagSet) {
	fs.BoolVar(&o.EnableConsole, flagEnableConsole, o.EnableConsole, "Enable Output Log to Consule")
	fs.StringVar(&o.FileName, flagFileName, o.FileName, "set log file name")
	fs.IntVar(&o.MaxSize, flagMaxSize, o.MaxSize, "Maximum Size in megabytes of the log file")
	fs.IntVar(&o.MaxAge, flagMaxAge, o.MaxAge, "Maximum number of days to retain old log files")
	fs.IntVar(&o.MaxFile, flagMaxFile, o.MaxFile, "Maximum number of old log files to retain")
	fs.BoolVar(&o.Compress, flagCompress, o.Compress, "Compress determines if the rotated log files should be compressed")

	fs.StringVar(&o.Level, flagLevel, o.Level, "Minimum log output `LEVEL`.")
	fs.BoolVar(&o.DisableCaller, flagDisableCaller, o.DisableCaller, "Disable output of caller information in the log.")
	fs.BoolVar(&o.DisableStacktrace, flagDisableStacktrace,
		o.DisableStacktrace, "Disable the log to record a stack trace for all messages at or above panic level.")
	fs.StringVar(&o.Format, flagFormat, o.Format, "Log output `FORMAT`, support plain or json format.")
	fs.BoolVar(&o.EnableColor, flagEnableColor, o.EnableColor, "Enable output ansi colors in plain format logs.")
	fs.BoolVar(
		&o.Development,
		flagDevelopment,
		o.Development,
		"Development puts the logger in development mode, which changes "+
			"the behavior of DPanicLevel and takes stacktraces more liberally.",
	)
	fs.StringVar(&o.Name, flagName, o.Name, "The name of the logger.")
}

func (o *Options) String() string {
	data, _ := json.Marshal(o)

	return string(data)
}
